<?php
/**
 * Created by PhpStorm.
 * User: syzhou
 * Date: 2019-09-02
 * Time: 14:57
 */

namespace App\Services;


use App\Project;
use App\ReviewerGroupProject;
use App\ReviewerGroupReviewer;
use App\ReviewerScoreProject;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class ReviewService
{
    /**
     * @param $data array 模式:
     * - reviewer_id: int|string, 必选。专家id
     * - project_id: int|string, 必选。项目id
     * - score: int, 可选。 总分
     * - is_evidence_material_valid： 1|0，可选。附件证明材料是否有效
     * - remark: string，可选。专家意见
     * - form_data: object|array|string, 可以是空对象"{}",不能是 null，示例:
     *
     * {
     * "scores": [
     * {
     * "key": "1",
     * "evaluation_index": "技术创新",
     * "standard_score": 60,
     * "real_score": 50,
     * "remarks": null
     * },
     * {
     * "key": "2",
     * "evaluation_index": "成果应用",
     * "standard_score": 15,
     * "real_score": 10,
     * "remarks": null
     * },
     * {
     * "key": "3",
     * "evaluation_index": "经济与社会效益",
     * "standard_score": 15,
     * "real_score": 13,
     * "remarks": null
     * },
     * {
     * "key": "4",
     * "evaluation_index": "推广应用前景",
     * "standard_score": 10,
     * "real_score": 10,
     * "remarks": null
     * }
     * ]
     * }
     * @return array
     */
    public function saveProjectScore($data)
    {
        $reviewerId = $data['reviewer_id'];
        $projectId = $data['project_id'];
        $formData = $data['form_data'];
        if (is_array($formData) || is_object($formData)) {
            $formData = json_encode($formData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
            $data['form_data'] = $formData;
        }
        $cols = ['score', 'form_data', 'is_evidence_material_valid', 'remark'];
        $values = array_filter($data, function ($key) use ($cols) {
            return in_array($key, $cols);
        }, ARRAY_FILTER_USE_KEY);
        if (empty($values)) {
            return [
                'ok' => false,
                'code' => 4,
                'msg' => "缺少评分数据"
            ];
        }
        try {
            $score_begin_at = ReviewerScoreProject::where('project_id', $projectId)
                ->where('reviewer_id', $reviewerId)
                ->value('score_begin_at');

            $values['score_time_cost'] = time() - $score_begin_at;
            ReviewerScoreProject::where('project_id', $projectId)
                ->where('reviewer_id', $reviewerId)
                ->update($values);
            return [
                'ok' => true,
                'code' => 0,
                'msg' => 'ok',
            ];
        } catch (\Exception $e) {
            Log::error('', ['exception' => $e]);
            return [
                'ok' => false,
                'code' => 2,
                'msg' => '数据库错误',
            ];
        }
    }

    public function submitProjectScore($reviewerId, $projectId)
    {
        $inTransaction = false;
        try {
            $reviewerGroupId = ReviewerGroupReviewer::where('reviewer_id', $reviewerId)->value('reviewer_group_id');
            $items = ReviewerScoreProject::where('project_id', $projectId)
                ->where('reviewer_group_id', $reviewerGroupId)
                ->get();
            $reviewerScoreProjects = [];
            foreach ($items as $item) {
                $reviewerScoreProjects[] = $item;
            }

            // 寻找待提交的项目-专家评分
            $targetReviewerScoreProject = null;
            foreach ($reviewerScoreProjects as $item) {
                if ($item->reviewer_id == $reviewerId) {
                    $targetReviewerScoreProject = $item;
                    break;
                }
            }
            if ($targetReviewerScoreProject === null) {
                return [
                    'ok' => false,
                    'code' => 3,
                    'msg' => "专家(#$reviewerId)-项目(#$projectId)关系不存在",
                ];
            }

            $scoreStatus = $targetReviewerScoreProject->scoreStatus();
            if ($scoreStatus['score_status_id'] != ReviewerScoreProject::SCORE_STATUS_ID_WAIT_FOR_SUBMIT) {
                return [
                    'ok' => false,
                    'code' => 4,
                    'msg' => "当前专家项目评分状态不是{ReviewerScoreProject::SCORE_STATUS_ID_WAIT_FOR_SUBMIT}，不能提交评分"
                ];
            }

            // 检查该项目所属专家组的专家是否都已提交打分
            $targetReviewerScoreProject->is_submitted = 1;
            $allReviewersSubmitted = true;
            foreach ($reviewerScoreProjects as $item) {
                $scoreStatus = $item->scoreStatus();
                Log::debug("project #$projectId, reviewer #{$item->reviewer_id}, scoreStatus #" . $scoreStatus['score_status_id']);
                if ($scoreStatus['score_status_id'] !== ReviewerScoreProject::SCORE_STATUS_ID_SUBMITTED) {
                    $allReviewersSubmitted = false;
                    break;
                }
            }
            Log::debug("project #$projectId, 所有专家已提交打分: " . (int)$allReviewersSubmitted);

            DB::beginTransaction();
            $inTransaction = true;
            $targetReviewerScoreProject->save();
            if (!$allReviewersSubmitted) {
                DB::commit();
                return [
                    'ok' => true,
                    'code' => 0,
                    'msg' => 'ok',
                ];
            }

            // 该项目所属专家组的专家都已提交打分，应修改专家组评分状态及项目状态
            $scores = array_map(function ($item) {
                return $item->score;
            }, $reviewerScoreProjects);

            /**
             * 20200602@1
             * @修改
             * @需求：每项成果网评得分为去掉最高分和最低分后的平均分
             * @time 2020-06-02
             * @start
             */
            $avgScore = $this->calcFinalScoreOfProject($scores);

//            $sumScore = array_reduce($scores, function ($sum, $score) {
//                return $sum + $score;
//            }, 0);
//
//            $avgScore = round($sumScore / count($scores), 2);

            /**
             * 20200602@1
             * @end
             */

            /**
             * 20200602@2
             * @修改
             * @需求：所有专家完成评分以后更改为 "已完成评分状态"
             * @time 2020-06-02
             * @start
             */
            $projectReviewerGroupScoreStatus = ReviewerGroupProject::SCORE_STATUS_SCORE_FINISHED;

//            $projectReviewerGroupScoreStatus = $scoresNormal ?
//                ReviewerGroupProject::SCORE_STATUS_TO_LEADER_REVIEW :
//                ReviewerGroupProject::SCORE_STATUS_ABNORMAL_SCORES;

            ReviewerGroupProject::where('project_id', $projectId)
                ->where('reviewer_group_id', $reviewerGroupId)
                ->update(['avg_score' => $avgScore, 'score_status' => $projectReviewerGroupScoreStatus]);

            /**
             * 20200602@2
             * @end
             */

            Log::debug("update reviewer_group_projects(project_id=$projectId, reviewerGroupId=$reviewerGroupId): avg_score=$avgScore, score_status=$projectReviewerGroupScoreStatus");

            $project = Project::find($projectId);
            $currentProjectStatus = $project->status_id;
            Log::debug("project #$projectId, currentStatus=$currentProjectStatus");
            $nextProjectStatus = null;

            if ($currentProjectStatus === Project::STATUS_EXPERT_REVIEW_TO_SCORE) {

                /**
                 * 20200602@3
                 * @修改
                 * @需求：所有专家完成评分以后更改为 "已完成评分，待提交至会评" 废弃 组长审核 及 分值异常
                 * @time 2020-06-02
                 * @start
                 */

                $nextProjectStatus = Project::STATUS_EXPERT_REVIEW_SCORE_FINISHED;
//                $nextProjectStatus = $scoresNormal ? Project::STATUS_EXPERT_REVIEW_TO_LEADER_REVIEW :
//                    Project::STATUS_EXPERT_REVIEW_ABNORMAL_SCORES;
                /**
                 * 20200602@3
                 * @end
                 */

            } else {  // 不应该
                Log::error("[ReviewService::submitProjectScore] 项目当前状态不符预期：project #$projectId, currentStatus=$currentProjectStatus");
                DB::rollBack();
                return [
                    'ok' => false,
                    'code' => 5,
                    'msg' => "项目当前状态预期为{Project::STATUS_EXPERT_REVIEW_TO_SCORE}，实际为$currentProjectStatus",
                ];
            }

            Log::debug("project #$projectId, currentStatus=$currentProjectStatus, nextStatus=$nextProjectStatus");
            $project->status_id = $nextProjectStatus;
            $project->avg_score = $avgScore;
            $project->save();
            DB::commit();
            return [
                'ok' => true,
                'code' => 0,
                'msg' => 'ok',
            ];
        } catch (\Exception $e) {
            Log::error('', ['exception' => $e]);
            if ($inTransaction) {
                try {
                    DB::rollBack();
                } catch (\Exception $exception) {

                }
            }
            return [
                'ok' => false,
                'code' => 2,
                'msg' => '数据库错误',
            ];
        }
    }

    /**
     * 项目分数结算
     * @param   array $scores 专家打分统计数组 [99,98,99,88,89]
     * @步骤：
     *      1.获取所有评分最高分和最低分
     *      2.移除最低最高分，取平均值
     * @time    2020-06-02
     * @author  lxy
     * @email   thesmallwhiteme@163.com
     * @return  float
     */
    private function calcFinalScoreOfProject(array $scores) : float {
        /**
         * 去掉一个最低分，去掉一个最高分，取平均分
         * @time 2020-06-02
         */
        $minScore = min($scores);
        $maxScore = max($scores);

        $sumScore = array_reduce($scores, function ($sum, $score) {
            return $sum + $score;
        }, 0);

        $sumScore = $sumScore - ( $minScore + $maxScore );
        $count = count($scores) - 2;
        return  round($sumScore / $count ,2);
    }

    /**
     * 获取评审专家的项目统计
     * @param null $largeGroupId
     * @return array
     */
    public function getReviewerProjectStats($largeGroupId = null)
    {
        // 获取专家的专家组信息
        $query = DB::table('reviewer_group_reviewers as a');
        if ($largeGroupId !== null) {
            $query->where('a.large_group_id', $largeGroupId);
        }
        $reviewerGroupInfo = $query->leftJoin('reviewer_groups as b', 'a.reviewer_group_id', '=', 'b.id')
            ->leftJoin('large_groups as c', 'a.large_group_id', '=', 'c.id')
            ->select('a.reviewer_id', 'a.reviewer_group_id', 'a.reviewer_order',
                'b.reviewer_group_order', 'b.name as reviewer_group_name',
                'c.order as large_group_order', 'c.name as large_group_name')
            ->get();
        $reviewerId2GroupInfo = [];
        foreach ($reviewerGroupInfo as $item) {
            $reviewerId2GroupInfo[$item->reviewer_id] = $item;
        }

        $query = DB::table('reviewer_score_projects as a');
        if ($largeGroupId !== null) {
            $query->where('a.large_group_id', $largeGroupId);
        }
        $rows = $query->leftJoin('users as b', 'a.reviewer_id', '=', 'b.id')
            ->select('a.reviewer_id', 'a.reviewer_group_id', 'a.large_group_id', 'a.is_submitted',
                'b.username', 'b.password', 'b.name', 'b.prof_title', 'b.tel', 'b.work_org_name')
            ->get();

        $result = []; // reviewer_id => reviewer data
        foreach ($rows as $row) {
            $reviewer_id = $row->reviewer_id;
            if (!isset($result[$reviewer_id])) {
                $reviewerGroupReviewer = $reviewerId2GroupInfo[$reviewer_id];
                $result[$reviewer_id] = [
                    'large_group_id' => $row->large_group_id,
                    'large_group_order' => $reviewerGroupReviewer->large_group_order,
                    'large_group_name' => $reviewerGroupReviewer->large_group_name,
                    'reviewer_group_id' => $row->reviewer_group_id,
                    'reviewer_group_order' => $reviewerGroupReviewer->reviewer_group_order,
                    'reviewer_group_name' => $reviewerGroupReviewer->reviewer_group_name,
                    'reviewer_id' => $row->reviewer_id,
                    'reviewer_order' => $reviewerGroupReviewer->reviewer_order,
                    'username' => $row->username,
                    'password' => $row->password,
                    'name' => $row->name,
                    'prof_title' => $row->prof_title,
                    'tel' => $row->tel,
                    'work_org_name' => $row->work_org_name,
                    'total_projects' => 0,
                    'finished_projects' => 0,
                    'unfinished_projects' => 0,
                ];
            }
            $result[$reviewer_id]['total_projects']++;
            if ($row->is_submitted == 1) {
                $result[$reviewer_id]['finished_projects']++;
            } else {
                $result[$reviewer_id]['unfinished_projects']++;
            }
        }
        usort($result, function ($a, $b) {
            $keys = ['large_group_order', 'reviewer_group_order', 'reviewer_order'];
            foreach ($keys as $key) {
                $diff = $a[$key] - $b[$key];
                if ($diff != 0) {
                    return $diff;
                }
            }
        });
        return $result;
    }

    public function onViewApplication($reviewerId, $projectId)
    {
        $this->updateScoreBeginAt($reviewerId, $projectId);
    }

    public function onViewAttachments($reviewerId, $projectId)
    {
        $this->updateScoreBeginAt($reviewerId, $projectId);
    }

    public function onScore($reviewerId, $projectId)
    {
        $this->updateScoreBeginAt($reviewerId, $projectId);
    }

    private function updateScoreBeginAt($reviewerId, $projectId)
    {
        ReviewerScoreProject::where('project_id', $projectId)
            ->where('reviewer_id', $reviewerId)
            ->where('score_begin_at', null)
            ->update(['score_begin_at' => time()]);
    }
}